﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Threading;
using System.IO;

namespace ClientPerformance
{
    public delegate void TrafficPatternUpdateDelegate(TrafficPatternInfo info);
    public delegate void UpdateStatusDelegate(string status);
    public delegate void FireEventDelegate();

    public static class WorkerThread
    {

        public static PeerService service;
        private static InstanceContext host;
        private static DuplexChannelFactory<IPeerPerformanceChannel> channelFactory;
        private static IPeerPerformanceChannel channel;
        private static string MeshPassword = "WCFPeerPerf";

        private static Timer sendTrafficTimer;
        private static Timer sendNeighborTimer;

        private static int messageId;
        private static int sendNeighborClock;

        public static event TrafficPatternUpdateDelegate TrafficPatternUpdated;
        public static event UpdateStatusDelegate StatusUpdate;
        public static event FireEventDelegate StopSendingTraffic;
        public static event FireEventDelegate StartSendingTraffic;

        public static void Run(object o)
        {
            string nodeName = o as string;
            sendNeighborClock = -1;

            service = new PeerService(nodeName);
            service.StartTrafficRecieved += new TrafficDelegate(service_StartTrafficRecieved);
            service.StopTrafficRecieved += new TrafficDelegate(service_StopTrafficRecieved);
            service.TrafficPatternUpdated += new TrafficPatternUpdateRecievedDelegate(service_TrafficPatternUpdated);

            host = new InstanceContext(service);


            host.Closed += new EventHandler(host_Closed);
            host.Faulted += new EventHandler(host_Faulted);
            //host.Opened += new EventHandler(host_Opened);
            //host.Opening += new EventHandler(host_Opening);

            try
            {
                // Open the host
                RaiseStatusUpdate("Opening...");
                host.BeginOpen(new AsyncCallback(HostOpened), null);
            }
            catch
            {
                RaiseStatusUpdate("Connection Failed - Check PeerResolver");
            }
        }

        static void service_TrafficPatternUpdated(TrafficPatternInfo info)
        {
            if (TrafficPatternUpdated != null)
                TrafficPatternUpdated(info);
        }

        static void service_StopTrafficRecieved()
        {
            StopTraffic( /*localOnly*/ true);
        }

        static void service_StartTrafficRecieved()
        {
            StartTraffic( /*localOnly*/ true);
        }

        private static void HostOpened(IAsyncResult ar)
        {
            RaiseStatusUpdate("Host Opened - opening channel...");


            // create the duplex channel to communicate via
            channelFactory = new DuplexChannelFactory<IPeerPerformanceChannel>(service, "WCFPeerPerformance");
            channelFactory.Credentials.Peer.MeshPassword = MeshPassword;
            channel = channelFactory.CreateChannel();

            // Filter only network messages
            RemoteOnlyMessagePropagationFilter myLocalFilter = new RemoteOnlyMessagePropagationFilter();
            PeerNode myPeerNode = channel.GetProperty<PeerNode>();
            myPeerNode.MessagePropagationFilter = myLocalFilter;

            // open the client channel
            channel.Faulted += new EventHandler(channel_Faulted);
            channel.Closed += new EventHandler(channel_Closed);

            try
            {
                channel.BeginOpen(new AsyncCallback(ChannelOpened), null);
            }
            catch
            {
                System.Diagnostics.Debugger.Break();
            }

        }

        #region Channel Events

        private static void channel_Closed(object sender, EventArgs e)
        {
            InternalAbort("Channel Closed");
        }

        private static void ChannelOpened(IAsyncResult ar)
        {
            if (channel.State == CommunicationState.Opened)
                RaiseStatusUpdate("Connected to Mesh"); 
            else
                RaiseStatusUpdate("Mesh " + channel.State.ToString()); 
        }

        private static void channel_Faulted(object sender, EventArgs e)
        {
            InternalAbort("Channel Faulted");
        }
        #endregion

        private static void InternalAbort(string message)
        {
            if (StopSendingTraffic != null)
                StopSendingTraffic();
            RaiseStatusUpdate(message);
            DeactivateTimers();
        }

        #region Host Events
        private static void host_Opening(object sender, EventArgs e)
        {
            //RaiseStatusUpdate("Host opening...");
        }

        private static void host_Opened(object sender, EventArgs e)
        {
            //RaiseStatusUpdate("Host Opened");
        }

        private static void host_Faulted(object sender, EventArgs e)
        {
            InternalAbort("Host Faulted");
        }

        private static void host_Closed(object sender, EventArgs e)
        {
            InternalAbort("Host Closed");
        }
        #endregion

        public static void UpdateTrafficPattern(TrafficPatternInfo info)
        {
            info.SessionId = Guid.NewGuid().ToString();
            service.DefaultPattern = info;

            service.DefaultPacket.SessionId = info.SessionId;
            service.DefaultPacket.Payload = PayloadFactory.Create(info.PacketSize);

            if (TrafficPatternUpdated != null)
                TrafficPatternUpdated(info);
        }

        public static void StartTraffic(bool localOnly)
        {
            if (!localOnly)
            {
                try
                {
                    channel.SendTrafficPattern(service.DefaultPattern);

                    channel.StartTraffic();
                }
                catch
                {
                    System.Diagnostics.Debugger.Break();
                }
            }

            ActivateTimers();
        }

        public static void StopTraffic(bool localOnly)
        {
            if (!localOnly)
                channel.StopTraffic();

            DeactivateTimers();


            if (StopSendingTraffic != null)
                StopSendingTraffic();
        }

        public static void Shutdown()
        {
            if (host.State == CommunicationState.Opened)
                host.Close();

            if (service.Logger.Length > 0)
            {
                File.WriteAllText("Logs_" + service.DefaultPacket.SourceName + ".txt", service.Logger.ToString());
                service.Logger = new StringBuilder(1024 * 5);
            }

            if (service.NeighborListLog.Length > 0)
            {
                File.WriteAllText("Graph_" + service.DefaultPacket.SourceName + ".txt", service.NeighborListLog.ToString());
                service.NeighborListLog = new StringBuilder(1024 * 5);
            }
        }

        #region Sending Timer
        private static void ActivateTimers()
        {
            int sendRate = service.DefaultPattern.SendRateInMilliseconds;

            if (sendTrafficTimer == null)
                sendTrafficTimer = new Timer(new TimerCallback(SendTrafficMessages_Fired), null, 0, sendRate);
            else
                sendTrafficTimer.Change(0, sendRate);

            // Warning: Tricky sync here - I want the neighbor timers to fire all at the 5, 10, 15 marks 
            // so figure out how far we are from that currentlyand adjust the start time
            int waitToStart = 5 - (DateTime.Now.Second % 5);
            if (sendNeighborTimer == null)
                sendNeighborTimer = new Timer(new TimerCallback(SendNeighborMessage_Fired), null, waitToStart, 15000);
            else
                sendNeighborTimer.Change(waitToStart, 15000);

            if (StartSendingTraffic != null)
                StartSendingTraffic();
        }

        private static void DeactivateTimers()
        {
            if (sendTrafficTimer != null)
                sendTrafficTimer.Change(Timeout.Infinite, Timeout.Infinite);

            if (sendNeighborTimer != null)
                sendNeighborTimer.Change(Timeout.Infinite, Timeout.Infinite);
        }

        private static void SendTrafficMessages_Fired(object o)
        {
            try
            {
                service.DefaultPacket.DateTimeSent = DateTime.Now;
                int current = Interlocked.Increment(ref messageId);
                service.DefaultPacket.MessageId = current;

                channel.SendPacket(service.DefaultPacket);

                TrafficPatternInfo info = service.DefaultPattern;
                if (info.MaxMessages == current)
                {
                    StopTraffic(  /*localOnly*/ true);
                    Console.Beep(440, 100);   // alert that we stopped
                }
            }
            catch (Exception ex)
            {
                File.AppendAllText("Errors.txt", string.Format("\nSendNeighborMessage: {0}\n{1}\n\n", ex.Message, ex.StackTrace));
            }
        }


        private static void SendNeighborMessage_Fired(object o)
        {
            //DateTime current = DateTime.Now;
            List<string> temp = service.GetNeighborListCopy();

            int tempClock = Interlocked.Increment(ref sendNeighborClock);

            foreach (var item in temp)
            {
                service.NeighborListLog.AppendLine(string.Format("{0}\t{1}\t{2}", tempClock, service.DefaultPacket.SourceName, item));
            }

            Thread.Sleep(1000);

            try
            {
                channel.SendNeighborMessage(new NeighborAlertMessage(service.DefaultPacket.SourceName, 1));
            }
            catch (Exception ex)
            {
                File.AppendAllText("Errors.txt", string.Format("\nSendNeighborMessage: {0}\n{1}\n\n", ex.Message, ex.StackTrace));
            }
        }
        #endregion

        

        private static void RaiseStatusUpdate(string status)
        {
            if (StatusUpdate != null)
                StatusUpdate(status);
        }
    }
}
